local assets =
{

    Asset("ANIM", "anim/yuemiheng_equipment.zip"),
    Asset("ATLAS", "images/inventoryimages/yuemiheng_spear.xml"), 
    Asset("IMAGE", "images/inventoryimages/yuemiheng_spear.tex"),
}

local prefabs =
{
    "yuemiheng_spear_fx",
}

-------GREEN STAFF-----------

local DESTSOUNDS =
{
    {   --magic
        soundpath = "dontstarve/common/destroy_magic",
        ing = { "nightmarefuel", "livinglog" },
    },
    {   --cloth
        soundpath = "dontstarve/common/destroy_clothing",
        ing = { "silk", "beefalowool" },
    },
    {   --tool
        soundpath = "dontstarve/common/destroy_tool",
        ing = { "twigs" },
    },
    {   --gem
        soundpath = "dontstarve/common/gem_shatter",
        ing = { "redgem", "bluegem", "greengem", "purplegem", "yellowgem", "orangegem" },
    },
    {   --wood
        soundpath = "dontstarve/common/destroy_wood",
        ing = { "log", "boards" },
    },
    {   --stone
        soundpath = "dontstarve/common/destroy_stone",
        ing = { "rocks", "cutstone" },
    },
    {   --straw
        soundpath = "dontstarve/common/destroy_straw",
        ing = { "cutgrass", "cutreeds" },
    },
}
local DESTSOUNDSMAP = {}
for i, v in ipairs(DESTSOUNDS) do
    for i2, v2 in ipairs(v.ing) do
        DESTSOUNDSMAP[v2] = v.soundpath
    end
end
DESTSOUNDS = nil

local function CheckSpawnedLoot(loot)
    if loot.components.inventoryitem ~= nil then
        loot.components.inventoryitem:TryToSink()
    else
        local lootx, looty, lootz = loot.Transform:GetWorldPosition()
        if ShouldEntitySink(loot, true) or TheWorld.Map:IsPointNearHole(Vector3(lootx, 0, lootz)) then
            SinkEntity(loot)
        end
    end
end

local function SpawnLootPrefab(inst, lootprefab)
    if lootprefab == nil then
        return
    end

    local loot = SpawnPrefab(lootprefab)
    if loot == nil then
        return
    end

    local x, y, z = inst.Transform:GetWorldPosition()

    if loot.Physics ~= nil then
        local angle = math.random() * TWOPI
        loot.Physics:SetVel(2 * math.cos(angle), 10, 2 * math.sin(angle))

        if inst.Physics ~= nil then
            local len = loot:GetPhysicsRadius(0) + inst:GetPhysicsRadius(0)
            x = x + math.cos(angle) * len
            z = z + math.sin(angle) * len
        end

        loot:DoTaskInTime(1, CheckSpawnedLoot)
    end

    loot.Transform:SetPosition(x, y, z)

	loot:PushEvent("on_loot_dropped", {dropper = inst})

    return loot
end
---comment
---@param staff ent
---@param target ent
---@param pos Vector3
---@param doer ent
local function destroystructure(staff, target, pos, doer)
    -- if not doer:HasTag('ymh_allow_staff_cast') then
    --     return false
    -- end
    local recipe = AllRecipes[target.prefab]
    if recipe == nil or FunctionOrValue(recipe.no_deconstruction, target) then
        --Action filters should prevent us from reaching here normally
        return
    end

    local ingredient_percent =
        (   (target.components.finiteuses ~= nil and target.components.finiteuses:GetPercent()) or
            (target.components.fueled ~= nil and target.components.inventoryitem ~= nil and target.components.fueled:GetPercent()) or
            (target.components.armor ~= nil and target.components.inventoryitem ~= nil and target.components.armor:GetPercent()) or
            1
        ) / recipe.numtogive

    --V2C: Can't play sounds on the staff, or nobody
    --     but the user and the host will hear them!
    local caster = staff.components.inventoryitem.owner

    for i, v in ipairs(recipe.ingredients) do
        if caster ~= nil and DESTSOUNDSMAP[v.type] ~= nil then
            caster.SoundEmitter:PlaySound(DESTSOUNDSMAP[v.type])
        end
        if string.sub(v.type, -3) ~= "gem" or string.sub(v.type, -11, -4) == "precious" then
            --V2C: always at least one in case ingredient_percent is 0%
            local amt = v.amount == 0 and 0 or math.max(1, math.ceil(v.amount * ingredient_percent))
            for n = 1, amt do
                SpawnLootPrefab(target, v.type)
            end
        end
    end

    if caster ~= nil then
        caster.SoundEmitter:PlaySound("dontstarve/common/staff_dissassemble")

        if caster.components.staffsanity then
            caster.components.staffsanity:DoCastingDelta(-TUNING.SANITY_MEDLARGE)
        elseif caster.components.sanity ~= nil then
            caster.components.sanity:DoDelta(-TUNING.SANITY_MEDLARGE)
        end
    end
    if staff.components.finiteuses then
        staff.components.finiteuses:Use(1)
    end
    

    if target.components.inventory ~= nil then
        target.components.inventory:DropEverything()
    end

    if target.components.container ~= nil then
		target.components.container:DropEverything(nil, true)
    end

    if target.components.spawner ~= nil and target.components.spawner:IsOccupied() then
        target.components.spawner:ReleaseChild()
    end

    if target.components.occupiable ~= nil and target.components.occupiable:IsOccupied() then
        local item = target.components.occupiable:Harvest()
        if item ~= nil then
            item.Transform:SetPosition(target.Transform:GetWorldPosition())
            item.components.inventoryitem:OnDropped()
        end
    end

    if target.components.trap ~= nil then
        target.components.trap:Harvest()
    end

    if target.components.dryer ~= nil then
        target.components.dryer:DropItem()
    end

    if target.components.harvestable ~= nil then
        target.components.harvestable:Harvest()
    end

    if target.components.stewer ~= nil then
        target.components.stewer:Harvest()
    end

	if target.components.constructionsite ~= nil then
		target.components.constructionsite:DropAllMaterials()
	end

    if target.components.inventoryitemholder ~= nil then
		target.components.inventoryitemholder:TakeItem()
	end

    target:PushEvent("ondeconstructstructure", caster)

	if not target.no_delete_on_deconstruct then
		if target.components.stackable ~= nil then
			--if it's stackable we only want to destroy one of them.
			target.components.stackable:Get():Remove()
		else
			target:Remove()
		end
	end
end

local function HasRecipe(guy)
    return guy.prefab ~= nil and AllRecipes[guy.prefab] ~= nil
end

local GREENHAUNT_CANT_TAGS = { "INLIMBO" }
local function onhauntgreen(inst)
    if math.random() <= TUNING.HAUNT_CHANCE_RARE then
        local target = FindEntity(inst, 20, HasRecipe, nil, GREENHAUNT_CANT_TAGS)
        if target ~= nil then
            destroystructure(inst, target)
            SpawnPrefab("collapse_small").Transform:SetPosition(target.Transform:GetWorldPosition())
            inst.components.hauntable.hauntvalue = TUNING.HAUNT_LARGE
            return true
        end
    end
    return false
end
-------------------

local function SetFxOwner(inst, owner)
    if inst._fxowner ~= nil and inst._fxowner.components.colouradder ~= nil then
        inst._fxowner.components.colouradder:DetachChild(inst.fx)
    end
    inst._fxowner = owner
    if owner ~= nil then
        inst.fx.entity:SetParent(owner.entity)
        inst.fx.Follower:FollowSymbol(owner.GUID, "swap_object", nil, nil, nil, true)
        inst.fx.components.highlightchild:SetOwner(owner)
        if owner.components.colouradder ~= nil then
            owner.components.colouradder:AttachChild(inst.fx)
        end
    else
        inst.fx.entity:SetParent(inst.entity)
        --For floating
        inst.fx.Follower:FollowSymbol(inst.GUID, "swap_spear", nil, nil, nil, true)
        inst.fx.components.highlightchild:SetOwner(inst)
    end
end

local function PushIdleLoop(inst)
    if inst.components.finiteuses:GetUses() > 0 then
        inst.AnimState:PushAnimation("idle")
    end
end

local function OnStopFloating(inst)
    if inst.components.finiteuses:GetUses() > 0 then
        inst.fx.AnimState:SetFrame(0)
        inst:DoTaskInTime(0, PushIdleLoop) --#V2C: #HACK restore the looping anim, timing issues
    end
end

local function onequip(inst, owner)
    owner.AnimState:ClearOverrideSymbol("swap_object")
    owner.AnimState:Show('ARM_carry')
    owner.AnimState:Hide('ARM_normal')


    SetFxOwner(inst, owner)
end

local function onunequip(inst, owner)
    owner.AnimState:ClearOverrideSymbol("swap_object")
    owner.AnimState:Hide('ARM_carry')
    owner.AnimState:Show('ARM_normal')


    SetFxOwner(inst, nil)
end

local function SetupComponents(inst)
    inst:AddComponent("equippable")
    inst.components.equippable:SetOnEquip(onequip)
    inst.components.equippable:SetOnUnequip(onunequip)

    -- inst:AddComponent("weapon")
    -- inst.components.weapon:SetDamage(0)
    -- inst.components.weapon:SetRange(8, 10)
end

local function onfinished(inst)
    inst:Remove()
end

local function onsave(inst,data)
    data.ymh_staff_upgrade = inst.ymh_staff_upgrade
    data.is_ymh_staff_upgrade = inst.is_ymh_staff_upgrade

    data.ymh_spear_upgrade = inst.ymh_spear_upgrade
    data.is_ymh_spear_upgrade = inst.is_ymh_spear_upgrade

    data.ymh_spear_finiteuse = inst.ymh_spear_finiteuse
    data.ymh_staff_finiteuse = inst.ymh_staff_finiteuse
end

local function onpreload(inst,data)
    if data then
        inst.ymh_staff_upgrade = data.ymh_staff_upgrade
        inst.is_ymh_staff_upgrade = data.is_ymh_staff_upgrade

        inst.ymh_spear_upgrade = data.ymh_spear_upgrade
        inst.is_ymh_spear_upgrade = data.is_ymh_spear_upgrade

        if inst.is_ymh_staff_upgrade then
            if not inst:HasTag('is_ymh_staff_upgrade') then
                inst:AddTag('is_ymh_staff_upgrade')
            end
            if inst.components.finiteuses then
                inst:RemoveComponent('finiteuses')
            end
        end

    end
end


local function fn()
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddSoundEmitter()
    inst.entity:AddNetwork()

    MakeInventoryPhysics(inst)

    inst.AnimState:SetBank("yuemiheng_spear")
    inst.AnimState:SetBuild("yuemiheng_equipment")
    inst.AnimState:PlayAnimation("idle", true)
    inst.AnimState:SetSymbolBloom("pb_energy_loop")
    inst.AnimState:SetSymbolBloom("stone")
    inst.AnimState:SetSymbolLightOverride("pb_energy_loop01", .5)
    inst.AnimState:SetSymbolLightOverride("pb_ray", .5)
    inst.AnimState:SetSymbolLightOverride("stone", .5)
    inst.AnimState:SetSymbolLightOverride("glow", .25)
    inst.AnimState:SetLightOverride(.1)

    inst:AddTag("rangedweapon")
    inst:AddTag("magicweapon")
    inst:AddTag("show_broken_ui")

    --weapon (from weapon component) added to pristine state for optimization
    inst:AddTag("weapon")

    inst:AddTag("yuemiheng_staff")

    inst.projectiledelay = FRAMES

    inst:AddComponent("floater")

    inst:AddTag("nopunch")

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then

        return inst
    end

    inst.ymh_staff_upgrade = {
        ["horrorfuel"] = 10,
        ["purebrilliance"] = 10,
    }

    inst.is_ymh_staff_upgrade = false

    local frame = math.random(inst.AnimState:GetCurrentAnimationNumFrames()) - 1
    inst.AnimState:SetFrame(frame)
    inst.fx = SpawnPrefab("yuemiheng_spear_fx")
    inst.fx.AnimState:SetFrame(frame)
    SetFxOwner(inst, nil)
    inst:ListenForEvent("floater_stopfloating", OnStopFloating)

    inst:AddComponent("inspectable") 

    inst:AddComponent("inventoryitem")
    inst.components.inventoryitem.imagename = "yuemiheng_spear"
    inst.components.inventoryitem.atlasname = "images/inventoryimages/yuemiheng_spear.xml" 




    inst:AddComponent("inspectable")
    inst:AddComponent("inventoryitem")

    SetupComponents(inst)

    local setbonus = inst:AddComponent("setbonus")
    setbonus:SetSetName(EQUIPMENTSETNAMES.LUNARPLANT)

    inst:AddComponent("finiteuses")
    inst.components.finiteuses:SetMaxUses(TUNING.MOD_YUEMIHENG.STAFF.FINITEUSE)
    inst.components.finiteuses:SetUses(TUNING.MOD_YUEMIHENG.STAFF.FINITEUSE)
    inst.components.finiteuses:SetOnFinished(onfinished)

    inst.fxcolour = {51/255,153/255,51/255}
    inst:AddComponent("spellcaster")
    inst.components.spellcaster.canuseontargets = true
    inst.components.spellcaster.canonlyuseonrecipes = true
    inst.components.spellcaster:SetSpellFn(destroystructure)
    -- inst.components.spellcaster:SetCanCastFn(function (doer, target, pos)
    --     return doer:HasTag('ymh_allow_staff_cast')
    -- end)


    inst.noplanarhitfx = true

    inst.OnSave = onsave
    inst.OnPreLoad = onpreload

    return inst
end

local function fxfn()
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddFollower()
    inst.entity:AddNetwork()

    inst:AddTag("FX")

    inst.AnimState:SetBank("yuemiheng_spear")
    inst.AnimState:SetBuild("yuemiheng_equipment")
    inst.AnimState:PlayAnimation("swap_loop", true)
    inst.AnimState:SetSymbolBloom("pb_energy_loop")
    inst.AnimState:SetSymbolBloom("stone")
    inst.AnimState:SetSymbolLightOverride("pb_energy_loop01", .5)
    inst.AnimState:SetSymbolLightOverride("pb_ray", .5)
    inst.AnimState:SetSymbolLightOverride("stone", .5)
    inst.AnimState:SetSymbolLightOverride("glow", .25)
    inst.AnimState:SetLightOverride(.1)

    inst:AddComponent("highlightchild")

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end

    inst:AddComponent("colouradder")

    inst.persists = false

    return inst
end

return Prefab("yuemiheng_staff", fn, assets, prefabs)